// User.cs
// Facebook/Framework/Service
// Copyright (c) 2007, Nikhil Kothari. All Rights Reserved.
//

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using Facebook.Service.Core;

namespace Facebook.Service {

    /// <summary>
    /// Represents a Facebook user.
    /// </summary>
    public sealed class User : FacebookEntity {

        /// <summary>
        /// The list of basic user information fields such as ID and name.
        /// </summary>
        public const string BasicFields =
            "uid,first_name,last_name,name";

        /// <summary>
        /// The list of all user information fields.
        /// </summary>
        public const string AllFields =
            "uid,first_name,last_name,name," +
            "birthday,sex," +
            "pic_square,pic_small,pic_big,pic," +
            "hometown_location,current_location,timezone," +
            "meeting_sex,meeting_for,relationship_status,significant_other_id," +
            "political,activities,interests,music,tv,movies,books,quotes,about_me," +
            "affiliations,religion," +
            "hs_info,education_history,work_history," +
            "profile_update_time,status,is_app_user,has_added_app," +
            "notes_count,wall_count";

        private bool _networksTransformed;
        private bool _currentLocationTransformed;
        private bool _hometownTransformed;
        private bool _genderTransformed;
        private bool _interestedInGendersTransformed;
        private bool _interestedInRelationshipsTransformed;
        private bool _workHistoryTransformed;
        private bool _educationHistoryTransformed;
        private bool _statusTransformed;
        private bool _idTransformed;

        internal User(Hashtable userState)
            : base(userState) {
        }

        /// <summary>
        /// The "About Me" section of the user's profile.
        /// </summary>
        public string About {
            get {
                return GetState<string>("about_me") ?? String.Empty;
            }
        }

        /// <summary>
        /// The Activities section of the user's profile.
        /// </summary>
        public string Activities {
            get {
                return GetState<string>("activities") ?? String.Empty;
            }
        }

        /// <summary>
        /// The user's birthday information as shared on the profile.
        /// </summary>
        public string Birthday {
            get {
                return GetState<string>("birthday") ?? String.Empty;
            }
        }

        /// <summary>
        /// The Books section of the user's profile.
        /// </summary>
        public string Books {
            get {
                return GetState<string>("books") ?? String.Empty;
            }
        }

        /// <summary>
        /// The Current Location entry on the user's profile.
        /// </summary>
        public Location CurrentLocation {
            get {
                if (_currentLocationTransformed == false) {
                    return TransformCurrentLocation();
                }
                return (Location)this["current_location"];
            }
        }

        /// <summary>
        /// Any education history listed on the user's profile.
        /// </summary>
        public ICollection<EducationHistory> EducationHistory {
            get {
                if (_educationHistoryTransformed == false) {
                    return TransformEducationHistory();
                }
                return (ICollection<EducationHistory>)this["education_history"];
            }
        }

        /// <summary>
        /// The user's first name.
        /// </summary>
        public string FirstName {
            get {
                return GetState<string>("first_name") ?? String.Empty;
            }
        }

        /// <summary>
        /// The user's gender as shared on the profile.
        /// </summary>
        public UserGender Gender {
            get {
                if (_genderTransformed == false) {
                    return TransformGender();
                }
                return (UserGender)this["sex"];
            }
        }

        /// <summary>
        /// Whether the user has added the current application to their facebook profile.
        /// </summary>
        public bool HasAddedApp {
            get {
                return GetState<bool>("has_added_app");
            }
        }

        /// <summary>
        /// The user's home town as shared on the profile.
        /// </summary>
        public Location Hometown {
            get {
                if (_hometownTransformed == false) {
                    return TransformHometown();
                }
                return (Location)this["hometown_location"];
            }
        }

        /// <summary>
        /// The unique ID of the user.
        /// </summary>
        public string ID {
            get {
                if (_idTransformed == false) {
                    return TransformID();
                }
                return GetState<string>("uid");
            }
        }

        /// <summary>
        /// The Interests section of the user's profile.
        /// </summary>
        public string Interests {
            get {
                return GetState<string>("interests") ?? String.Empty;
            }
        }

        /// <summary>
        /// The list of desired relationship genders as shared on the user's profile.
        /// </summary>
        public ICollection<UserGender> InterestedInGenders {
            get {
                if (_interestedInGendersTransformed == false) {
                    TransformInterestedInGenders();
                }
                return (ICollection<UserGender>)this["meeting_sex"];
            }
        }

        /// <summary>
        /// The list of desired relationship types as shared on the user's profile.
        /// </summary>
        public ICollection<string> InterestedInRelationships {
            get {
                if (_interestedInRelationshipsTransformed == false) {
                    TransformInterestedInRelationships();
                }
                return (ICollection<string>)this["meeting_for"];
            }
        }

        /// <summary>
        /// Whether the user has used the current application.
        /// </summary>
        public bool IsAppUser {
            get {
                return GetState<bool>("is_app_user");
            }
        }

        /// <summary>
        /// The URL of the larger version of the user's profile picture.
        /// </summary>
        public string LargePictureUrl {
            get {
                return GetState<string>("pic_big") ?? String.Empty;
            }
        }

        /// <summary>
        /// The user's last name.
        /// </summary>
        public string LastName {
            get {
                return GetState<string>("last_name") ?? String.Empty;
            }
        }

        /// <summary>
        /// The Movies section of the user's profile.
        /// </summary>
        public string Movies {
            get {
                return GetState<string>("movies") ?? String.Empty;
            }
        }

        /// <summary>
        /// The Music section of the user's profile.
        /// </summary>
        public string Music {
            get {
                return GetState<string>("music") ?? String.Empty;
            }
        }

        /// <summary>
        /// The user's full name.
        /// </summary>
        public string Name {
            get {
                return GetState<string>("name") ?? String.Empty;
            }
        }

        /// <summary>
        /// The list of networks that the user is affiliated with.
        /// </summary>
        public ICollection<UserNetwork> Networks {
            get {
                if (_networksTransformed == false) {
                    return TransformNetworks();
                }
                return (ICollection<UserNetwork>)this["affiliations"];
            }
        }

        /// <summary>
        /// The URL of the normal sized version of the user's profile picture.
        /// </summary>
        public string PictureUrl {
            get {
                return GetState<string>("pic") ?? String.Empty;
            }
        }

        /// <summary>
        /// The user's political view as shared on the profile.
        /// </summary>
        public string PoliticalView {
            get {
                return GetState<string>("political") ?? String.Empty;
            }
        }

        /// <summary>
        /// The Quotes section of the user's profile.
        /// </summary>
        public string Quotes {
            get {
                return GetState<string>("quotes") ?? String.Empty;
            }
        }

        /// <summary>
        /// The user's relationship status as shared on the profile.
        /// </summary>
        public string Relationship {
            get {
                return GetState<string>("relationship_status") ?? String.Empty;
            }
        }

        /// <summary>
        /// The user's religious views as shared on the profile.
        /// </summary>
        public string Religion {
            get {
                return GetState<string>("religion") ?? String.Empty;
            }
        }

        /// <summary>
        /// The ID of the user related to the user in their relationship status.
        /// </summary>
        public string RelatedUserID {
            get {
                return GetState<string>("significant_other_id") ?? String.Empty;
            }
        }

        /// <summary>
        /// The URL of the small version of the user's profile picture.
        /// </summary>
        public string SmallPictureUrl {
            get {
                return GetState<string>("pic_small") ?? String.Empty;
            }
        }

        /// <summary>
        /// The URL of the square section of the user's profile picture.
        /// </summary>
        public string SquarePictureUrl {
            get {
                return GetState<string>("pic_square") ?? String.Empty;
            }
        }

        /// <summary>
        /// The latest status associated with the user.
        /// </summary>
        public UserStatus Status {
            get {
                if (_statusTransformed == false) {
                    return TransformStatus();
                }
                return (UserStatus)this["status"];
            }
        }

        /// <summary>
        /// The user's time zone offset.
        /// </summary>
        public int TimeZoneOffset {
            get {
                return GetState<int>("timezone");
            }
        }

        /// <summary>
        /// The TV Shows section of the user's profile.
        /// </summary>
        public string TVShows {
            get {
                return GetState<string>("tv") ?? String.Empty;
            }
        }

        /// <summary>
        /// The number of wall posts on the user's profile.
        /// </summary>
        public int WallPosts {
            get {
                return GetState<int>("wall_count");
            }
        }

        /// <summary>
        /// Any work history listed on the user's profile.
        /// </summary>
        public ICollection<WorkHistory> WorkHistory {
            get {
                if (_workHistoryTransformed == false) {
                    return TransformWorkHistory();
                }
                return (ICollection<WorkHistory>)this["work_history"];
            }
        }

        private Location TransformCurrentLocation() {
            Debug.Assert(_currentLocationTransformed == false);

            Location currentLocation = null;
            
            Hashtable locationState = (Hashtable)this["current_location"];
            if (locationState != null) {
                currentLocation = new Location(locationState);
            }

            this["current_location"] = currentLocation;
            _currentLocationTransformed = true;

            return currentLocation;
        }

        private ICollection<EducationHistory> TransformEducationHistory() {
            Debug.Assert(_educationHistoryTransformed == false);

            List<EducationHistory> history = null;

            object data = this["education_history"];
            if (data is ArrayList) {
                ArrayList historyList = (ArrayList)data;
                int count = historyList.Count;

                history = new List<EducationHistory>(count);

                for (int i = 0; i < count; i++) {
                    Debug.Assert(historyList[i] is Hashtable);
                    history.Add(new EducationHistory((Hashtable)historyList[i]));
                }
            }
            else if (data is Hashtable) {
                history = new List<EducationHistory>(1);
                history.Add(new EducationHistory((Hashtable)data));
            }
            else {
                history = new List<EducationHistory>(0);
            }

            this["education_history"] = history;
            _educationHistoryTransformed = true;

            return history;
        }

        private UserGender TransformGender() {
            Debug.Assert(_genderTransformed == false);

            UserGender gender = UserGender.NotSpecified;
            string state = GetState<string>("sex") ?? String.Empty;

            if (state.Length != 0) {
                if (state.Equals("male")) {
                    gender = UserGender.Male;
                }
                else if (state.Equals("female")) {
                    gender = UserGender.Female;
                }
            }

            this["sex"] = gender;
            _genderTransformed = true;

            return gender;
        }

        private Location TransformHometown() {
            Debug.Assert(_hometownTransformed == false);

            Location hometown = null;

            Hashtable locationState = (Hashtable)this["hometown_location"];
            if (locationState != null) {
                hometown = new Location(locationState);
            }

            this["hometown_location"] = hometown;
            _hometownTransformed = true;

            return hometown;
        }

        private string TransformID() {
            Debug.Assert(_idTransformed == false);

            string id = FacebookResponse.CoerceString(this["uid"]);

            this["uid"] = id;
            _idTransformed = true;

            return id;
        }

        private ICollection<UserGender> TransformInterestedInGenders() {
            Debug.Assert(_interestedInGendersTransformed == false);

            ArrayList state = this["meeting_sex"] as ArrayList;
            List<UserGender> genders = new List<UserGender>();

            if (state != null) {
                for (int i = 0; i < state.Count; i++) {
                    if (state[i].Equals("male")) {
                        genders.Add(UserGender.Male);
                    }
                    else if (state[i].Equals("female")) {
                        genders.Add(UserGender.Female);
                    }
                }
            }

            this["meeting_sex"] = genders;
            _interestedInGendersTransformed = true;

            return genders;
        }

        private ICollection<string> TransformInterestedInRelationships() {
            Debug.Assert(_interestedInRelationshipsTransformed == false);

            ArrayList state = this["meeting_for"] as ArrayList;
            ICollection<string> relationships = null;

            if (state != null) {
                relationships = (string[])state.ToArray(typeof(string));
            }
            else {
                relationships = new string[0];
            }

            this["meeting_for"] = relationships;
            _interestedInRelationshipsTransformed = true;

            return relationships;
        }

        private ICollection<UserNetwork> TransformNetworks() {
            Debug.Assert(_networksTransformed == false);

            ArrayList affiliations = (ArrayList)this["affiliations"];
            int count = (affiliations != null) ? affiliations.Count : 0;

            List<UserNetwork> networks = new List<UserNetwork>(count);

            for (int i = 0; i < count; i++) {
                Debug.Assert(affiliations[i] is Hashtable);
                networks.Add(new UserNetwork((Hashtable)affiliations[i]));
            }

            this["affiliations"] = networks;
            _networksTransformed = true;

            return networks;
        }

        private UserStatus TransformStatus() {
            Debug.Assert(_statusTransformed == false);

            UserStatus status = null;

            Hashtable statusState = (Hashtable)this["status"];
            if (statusState != null) {
                status = new UserStatus(statusState);
            }

            this["status"] = status;
            _statusTransformed = true;

            return status;
        }

        private ICollection<WorkHistory> TransformWorkHistory() {
            Debug.Assert(_workHistoryTransformed == false);

            List<WorkHistory> history = null;

            object data = this["work_history"];
            if (data is ArrayList) {
                ArrayList historyList = (ArrayList)data;
                int count = historyList.Count;

                history = new List<WorkHistory>(count);

                for (int i = 0; i < count; i++) {
                    Debug.Assert(historyList[i] is Hashtable);
                    history.Add(new WorkHistory((Hashtable)historyList[i]));
                }
            }
            else if (data is Hashtable) {
                history = new List<WorkHistory>(1);
                history.Add(new WorkHistory((Hashtable)data));
            }
            else {
                history = new List<WorkHistory>(0);
            }

            this["work_history"] = history;
            _workHistoryTransformed = true;

            return history;
        }
    }
}
